﻿/***********************************************************************
 * Module:  CPFLauncher.cpp
 * Author:  ZHANGHASI
 * Modified: 2020年12月7日 下午 3:50:05
 * Purpose: Implementation of the class CPFLauncher
 ***********************************************************************/
#include <io.h>
#include <direct.h>


#ifdef LINUX_OS
#include <dlfcn.h>
#elif MAC_OS
#include <dlfcn.h>
#else
#include <windows.h>
#endif 


#include "CPFLauncher.h"
#include "CPFActivator.h"

// 默认插件路径
const std::string CONST_DEFAULT_PLUGINS_PATH = "plugins";


CPFLauncher* CPFLauncher::instance()
{
    std::mutex m1;
    m1.lock();
    static CPFLauncher* pLauncher = nullptr;

    if (nullptr == pLauncher)
    {
        pLauncher = new CPFLauncher();
    }
    m1.unlock();

    return pLauncher;
}

CPFLauncher::CPFLauncher()
{
    init();
}

CPFLauncher::~CPFLauncher()
{

}

void CPFLauncher::init()
{
    // 添加默认搜索路径
    m_vectPluginPath.push_back(CONST_DEFAULT_PLUGINS_PATH);
    // 创建默认插件目录
    createDefaultDir();


}

void CPFLauncher::createDefaultDir()
{
    // 创建临时目录
    if (0 != _access(CONST_DEFAULT_PLUGINS_PATH.c_str(), 0))
    {
        // if this folder not exist, create a new one.
        _mkdir(CONST_DEFAULT_PLUGINS_PATH.c_str());   // 返回 0 表示创建成功，-1 表示失败
        //换成 ::_mkdir  ::_access 也行，不知道什么意思
    }
}
// 从插件路径下搜索插件
std::string CPFLauncher::searchPluginFile(std::string plugin)
{
    std::string strResult;

    for (const std::string& path : m_vectPluginPath)
    {
        std::vector<std::string> files;

        getFiles(path, files);

        plugin = path + "/" + plugin + CPF_PLUGIN_SUFFIX;
        std::vector<std::string>::iterator iterResult =
            std::find_if(files.begin(), files.end(),
            [plugin](const std::string& val){ return val == plugin; });

        if (iterResult != files.end())
        {
            // path/xxxx.dll or path/xxxx.so or path/xxxx.dylib
            strResult = plugin;
            break;
        }
    }

    return strResult;
}
// 获取指定路径下插件文件，不包含子文件夹
#ifdef LINUX_OS
void CPFLauncher::getFiles(std::string path, std::vector<std::string>& files)
{
    DIR *dir;
    struct dirent *ptr;
    if ((dir = opendir(path.c_str())) == NULL)
    {
        perror("Open dir error...");
        return;
    }

    while ((ptr = readdir(dir)) != NULL)
    {
        if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0)    ///current dir OR parrent dir
            continue;
        else if (ptr->d_type == 8)    ///file
        {
            std::string strFile = ptr->d_name;
            if (strFile.substr(strFile.length() - std::string(CPF_PLUGIN_SUFFIX).length()) == CPF_PLUGIN_SUFFIX)
            {
                files.push_back(strFile);
            }
        }
        else
        {
            continue;
        }
    }
    closedir(dir);
}
#else
void CPFLauncher::getFiles(std::string path, std::vector<std::string>& files)
{
    std::string fileType = CPF_PLUGIN_SUFFIX;
    // 文件句柄
    long hFile = 0;
    // 文件信息
    struct _finddata_t fileinfo;

    std::string strTemp;

    if ((hFile = _findfirst(strTemp.assign(path).append("/*" + fileType).c_str(), &fileinfo)) != -1)
    {
        do
        {
            // 保存文件的全路径
            files.push_back(strTemp.assign(path).append("/").append(fileinfo.name));

        } while (_findnext(hFile, &fileinfo) == 0);  //寻找下一个，成功返回0，否则-1

        _findclose(hFile);
    }
}
#endif

bool CPFLauncher::load(std::string name)
{
    bool bRet = false;

    std::string plugin = searchPluginFile(name);
    void* hinst = nullptr;
#ifdef LINUX_OS
    hinst = ::dlopen(path.c_str(), RTLD_NOW);
    if (hinst)
    {
        bRet = true;
    }
    else
    {
        // const char* errorString = ::dlerror();
    }
#elif MAC_OS
    hinst = ::dlopen(path.c_str(), RTLD_NOW);
    if (hinst)
    {
        bRet = true;
    }
    else
    {
        // const char* errorString = ::dlerror();
    }
#else
    //plugin = "E:/workfolder/CPF/bin_debug/plugins/TestPlugin.dll";
    hinst = LoadLibraryA(plugin.c_str());

    if (hinst)
    {
        bRet = true;
    }
    else
    {
    }
#endif
    if (hinst && bRet)
    {
        m_hassLabraryHand.insert(std::make_pair(name, hinst));

        CPF_PLUGIN_FUNCTION_INSTANCE pActivatorFunc =
            reinterpret_cast<CPF_PLUGIN_FUNCTION_INSTANCE>(getSymbol(hinst, CPF_CREATE_INSTANCE));
        if (pActivatorFunc)
        {
            CPFActivator* pActivator = dynamic_cast<CPFActivator*>(pActivatorFunc());

            if (pActivator)
            {
                m_hashActivator.insert(std::make_pair(name, pActivator));
                bRet = startActivator(name);
            }
            else
            {
                bRet = false;
            }
        }
        else
        {
            bRet = false;
        }
    }
    else
    {
        bRet = false;
    }

    return bRet;
}

void* CPFLauncher::getSymbol(void* handle, const std::string& symbol)
{
    void* pResult = nullptr;
    if (nullptr == handle)
    {
        return pResult;
    }
    
#ifdef LINUX_OS
    pResult = (void*)(::dlsym(handle, symbol.c_str()));
#elif MAC_OS
    pResult = (void*)(::dlsym(handle, symbol.c_str()));
#else
    pResult = (void*)(::GetProcAddress((HMODULE)handle, symbol.c_str()));
#endif
    return pResult;
}

bool CPFLauncher::stopActivator(std::string name)
{
    bool bRet = false;

    auto iter = m_hashActivator.find(name);

    if (iter != m_hashActivator.end())
    {
        CPFActivator* pActivator = iter->second;

        bRet = pActivator->stop();
    }
    
    return bRet;
}

bool CPFLauncher::startActivator(std::string name)
{
    bool bRet = false;

    auto iter = m_hashActivator.find(name);

    if (iter != m_hashActivator.end())
    {
        CPFActivator* pActivator = iter->second;

        bRet = pActivator->start();
    }

    return bRet;
}

bool CPFLauncher::unloadHandle(std::string name)
{
    bool bRet = false;
    auto iter = m_hassLabraryHand.find(name);

    if (iter != m_hassLabraryHand.end())
    {
        int nRet = 0;
        void* handle = iter->second;
#ifdef LINUX_OS 
        nRet = ::dlclose(handle);
#elif MAC_OS
        nRet = ::dlclose(handle);
#else
        nRet = ::FreeLibrary((HMODULE)handle);
#endif
        if (!nRet)
        {
            m_hassLabraryHand.erase(iter);
            bRet = true;
        }
    }
    return bRet;
}

bool CPFLauncher::unload(std::string name)
{
    bool bRet = false;
    
    bRet = stopActivator(name);

    if (bRet)
    {
        bRet = unloadHandle(name);
    }

    return bRet;
}

bool CPFLauncher::start(void)
{
   // TODO : implement
    return false;
}

bool CPFLauncher::stop(void)
{
   // TODO : implement
    return false;
}

void CPFLauncher::addPath(std::string path)
{
    if (path == CONST_DEFAULT_PLUGINS_PATH)
    {
        return;
    }

    std::vector<std::string>::iterator iterResult =
        std::find_if(m_vectPluginPath.begin(), m_vectPluginPath.end(),
        [path](const std::string& val){ return val == path; });
    if (iterResult == m_vectPluginPath.end())
    {
        m_vectPluginPath.push_back(path);
    }
}

void CPFLauncher::removePath(std::string path)
{
    if (path == CONST_DEFAULT_PLUGINS_PATH)
    {
        return;
    }
    std::vector<std::string>::iterator iterResult =
        std::find_if(m_vectPluginPath.begin(), m_vectPluginPath.end(),
        [path](const std::string& val){ return val == path; });
    if (iterResult == m_vectPluginPath.end())
    {
        m_vectPluginPath.erase(iterResult);
    }
}

std::string CPFLauncher::defaultPath(void)
{
    return CONST_DEFAULT_PLUGINS_PATH;
}